{------------------------------------------------------------------------------
#
#	Apple Macintosh Developer Technical Support
#
#	Exception handling for MPW Pascal, MacApp and MPW C
#
#	UFailure (aka Signals) - ÒExceptional code, with a few exceptions.Ó
#
#	UFailure.inc1.p	-	Pascal source - the IMPLEMENTATION
#
#	Copyright © 1985-1988 Apple Computer, Inc.
#	All rights reserved.
#
#	Versions:	1.0					11/88
#
#	Components:	UFailure.p			November 1, 1988
#				UFailure.h			November 1, 1988
#				UFailure.inc1.p		November 1, 1988
#				UFailure.a			November 1, 1988
#				TestCignal.c		November 1, 1988
#				TestCignal.make		November 1, 1988
#				TestSignal.p		November 1, 1988
#				TestSignal.make		November 1, 1988
#
#	UFailure (or Signals) is a set of exception handling routines suitable for
#	use with MacApp, MPW C, and MPW Pascal. It is a jazzed-up version of the MacApp
#	UFailure unit. There is a set of C interfaces to it as well.
#
------------------------------------------------------------------------------}


VAR
	{$PUSH}
	{$Z+}	{make gTopHandler accessable to assembly code}
	gTopHandler:		PFailInfo;	{linked list of failure handlers}
 	gInitHandler:		ProcPtr;
	{$POP}


PROCEDURE InitUFailure; EXTERNAL;
	{ Allocates the heap block for CatchSignals and initializes the global
		variables used by the unit. }

PROCEDURE InitSignals; EXTERNAL;
	{ Calls InitUFailure. It also sets up the A6 for the main level of Pascal,
		so it must be called from the outermost level of Pascal. }


FUNCTION CatchSignal: INTEGER; EXTERNAL;
	{ Until the procedure which encloses this call returns, this will catch
		subsequent Signal calls, returning the code passed to Signal.  When
		CatchSignal is encountered initially, it returns a code of zero.  These
		calls may "nest"; i.e. you may have multiple CatchSignals in one procedure.
		Each nested CatchSignal call uses 72 bytes of heap space.
		If you signal with SignalMessage and pass in a non-zero message you should use
		CatchHandler instead so you have a way of getting at the message. }


PROCEDURE FreeSignal; EXTERNAL;
	{ This undoes the effect of the last CatchSignal.  A Signal will then invoke
		the CatchSignal prior to the last one. }


PROCEDURE Signal(code: INTEGER); EXTERNAL;
	{ Returns control to the point of the last CatchSignal.  The program will
		then behave as though that CatchSignal had returned with the code parameter
		supplied to Signal. If CatchHandler is catching, the message parameter will be 0. }


PROCEDURE SignalMessage(code: INTEGER; message: LONGINT); EXTERNAL;
	{ Returns control to the point of the last CatchSignal/CatchFailures.
		If CatchFailures is catching, the message parameter will be returned. }
		
		
{-----------------------------------+
|	MacApp stuff					|
+-----------------------------------}


{-----------------------------------+ 
|	External Declarations			|
+-----------------------------------}
PROCEDURE CatchFailures (VAR fi: FailInfo;
						PROCEDURE Handler(e: INTEGER; m: LONGINT)); EXTERNAL;

PROCEDURE DoFailure(pf: PFailInfo); EXTERNAL;

{-----------------------------------+ 
|	CallInitHandler					|
+-----------------------------------}
PROCEDURE CallInitHandler (error: INTEGER; message: LONGINT; p: ProcPtr);
		INLINE	$205F,		{MOVE.L		(A7)+,A0	}
				$4E90;		{JMP		(A0)		}

{$IFC UsingMacApp}
{$S MAMain}
{$ENDC}
{-----------------------------------+ 
|	FailMemError					|
+-----------------------------------}
PROCEDURE FailMemError;
VAR
	e:	OSErr;
{$IFC qDebug}
	s:		Str255;
{$ENDC}
BEGIN
	e := MemError;

{$IFC UsingMacApp}
{$IFC qDebug}
	IF gAskFailure AND (e = noErr) AND CanReadLn THEN
		BEGIN
{$%+}
		GetMethodName(%_GetA6+4, s);
{$%-}
		e := ReadInteger(CONCAT('FailMemError called by ', s, '.  Enter return error: '));
		END;
{$ENDC qDebug}
{$ENDC UsingMacApp}

	IF e <> noErr THEN
		Failure(e, 0);
END {FailMemError};


{$IFC UsingMacApp}
{$S MAMain}
{$ENDC}
{-----------------------------------+ 
|	FailNIL							|
+-----------------------------------}
PROCEDURE FailNIL (p: UNIV Ptr);
BEGIN
	{ no check for gAskFailure here, since we do this when objects are created. }
	IF p = NIL THEN
		Failure(memFullErr, 0);
END {FailNIL};


{$IFC UsingMacApp}
{$S MAMain}
{$ENDC}
{-----------------------------------+ 
|	FailNewMessage					|
+-----------------------------------}
PROCEDURE FailNewMessage (error: INTEGER; oldMessage, newMessage: LONGINT);
BEGIN
	IF oldMessage = 0 THEN
		oldMessage := newMessage;
	Failure(error, oldMessage);
END {FailNewMessage};


{$IFC UsingMacApp}
{$S MAMain}
{$ENDC}
{-----------------------------------+ 
|	FailOSErr						|
+-----------------------------------}
PROCEDURE FailOSErr (error: INTEGER);

{$IFC qDebug}
VAR
	s:		Str255;
{$ENDC}

BEGIN
{$IFC UsingMacApp}
{$IFC qDebug}
	IF gAskFailure AND (error = noErr) AND CanReadLn THEN
		BEGIN
{$%+}
		GetMethodName(%_GetA6+4, s);
{$%-}
		error := ReadInteger(CONCAT('FailOSErr called by ', s, '.  Enter return error: '));
		END;
{$ENDC qDebug}
{$ENDC UsingMacApp}

	IF error <> noErr THEN
		Failure(error, 0);
END {FailOSErr};


{$IFC UsingMacApp}
{$S MAMain}
{$ENDC}
{-----------------------------------+ 
|	FailResError					|
+-----------------------------------}
PROCEDURE FailResError;
VAR
	e:	OSErr;
{$IFC qDebug}
	s:		Str255;
{$ENDC}
BEGIN
	e := ResError;

{$IFC UsingMacApp}
{$IFC qDebug}
	IF gAskFailure AND (e = noErr) AND CanReadLn THEN
		BEGIN
{$%+}
		GetMethodName(%_GetA6+4, s);
{$%-}
		e := ReadInteger(CONCAT('FailResError called by ', s, '.  Enter return error: '));
		END;
{$ENDC qDebug}
{$ENDC UsingMacApp}

	IF e <> noErr THEN
		Failure(e, 0);
END {FailResError};


{$IFC UsingMacApp}
{$S MAMain}
{$ENDC}
{-----------------------------------+ 
|	Failure							|
+-----------------------------------}
PROCEDURE Failure (error: INTEGER; message: LONGINT);
VAR
	pf: 	PFailInfo;
	ih: 	ProcPtr;
	pc:		LONGINT;
{$IFC UsingMacApp}
{$IFC qDebug}
	cl: 	String8;
	me: 	String8;
	seg:	INTEGER;
	who:	STRING[17];
{$ENDC qDebug}
{$ENDC UsingMacApp}
BEGIN
	pf := gTopHandler;

	IF pf <> NIL THEN
		BEGIN
{$IFC UsingMacApp}
{$IFC qDebug}
		pc := pf^.whoPC;
		GetProcname(LONGINT(@pc), cl, me);
		who := CONCAT(cl, '.', me);
		IF cl = kSpace8 THEN
			who[9] := ' ';
		
		Writeln('Failure caught by ', who);
		Writeln('        error = ', error:1, ' message = ', message:1,
				' (', BSR(message, 16):1, '/', BAND(message, $0000FFFF):1, ')');
{$ENDC qDebug}
{$ENDC UsingMacApp}

	{* RBB removed the line 
		gTopHandler := pf^.nextInfo;
	  on 9/26/88 since DoFailure calls FreeSignal first thing *}
		pf^.error := error;
		pf^.message := message;
		DoFailure(pf);			{Go execute the failure handler}
		END
	ELSE IF gInitHandler <> NIL THEN
		BEGIN
		ih := gInitHandler;
		gInitHandler := NIL;
		CallInitHandler(error, message, ih);

		ExitToShell;
		END
	ELSE
		BEGIN
{$IFC UsingMacApp}
{$IFC qDebug}
		ProgramBreak('Failure called, but no handler!');
{$ENDC qDebug}
{$ELSEC}
	Debugger;
{$ENDC UsingMacApp}
		END;
END {Failure};


{$IFC UsingMacApp}
{$IFC qDebug}
{$IFC qTrace}{$D+}{$ENDC}
{$S MADebug}
{-----------------------------------+ 
|	ProgramBreak					|
+-----------------------------------}
PROCEDURE ProgramBreak (grievance: Str255);
	{ ProgramBreak: Your app can call this when it comes to a situation that you do not expect
		and cannot handle gracefully.  It beeps and displays a message.  If called before
		there is a WriteLn window, it calls OBJFail, which goes into an infinite loop.
		Otherwise, it enters our debugger. }
VAR
	synthRec:	RECORD
				mode:		INTEGER;
				triplet:	Tone;
				endTriplet: Tone;
				END;

BEGIN
{$IFC FALSE}
	WITH synthRec, triplet DO
		BEGIN
		mode := swMode;

		count := 445;
		amplitude := 100;
		duration := 25;

		endTriplet.count := 0;
		endTriplet.amplitude := 0;
		endTriplet.duration := 0;
		END;

	StartSound(@synthRec, SIZEOF(synthRec), Pointer(-1));
{$ENDC}
	SysBeep(2);

	WWForceOutput(forceOn, forceUnchanged);
	WriteLn('ProgramBreak: ', grievance);
	WWEndForce;

{$IFC qTrace}
	TRCBreak;
{$ELSEC}
	OBJFail(kFailNone);
{$ENDC}
END {ProgramBreak};
{$IFC qTrace}{$D++}{$ENDC}


{$IFC qTrace}{$D+}{$ENDC}
{$S MADebug}
{-----------------------------------+ 
|	ProgramReport					|
+-----------------------------------}
PROCEDURE ProgramReport (grievance: Str255; break: BOOLEAN);

BEGIN
	Writeln(grievance);
	IF break THEN
		TRCBreak;
END {ProgramReport};
{$IFC qTrace}{$D++}{$ENDC}
{$ENDC qDebug}
{$ENDC UsingMacApp}


{$IFC UsingMacApp}
{$S MAInit}
{$IFC qTrace}{$D+}{$ENDC}
{-----------------------------------+ 
|	SetInitHandler					|
+-----------------------------------}
PROCEDURE SetInitHandler (handler: ProcPtr);
BEGIN
	gInitHandler := handler;
END {SetInitHandler};
{$IFC qTrace}{$D++}{$ENDC}
{$ENDC UsingMacApp}


{We assume that the programmer passes in the correct FailInfo record; ie. the one that is the
	top of the stack.}
{$IFC UsingMacApp}
{$S MAMain}
{$ENDC}
{-----------------------------------+ 
|	Success							|
+-----------------------------------}
PROCEDURE Success (VAR fi: FailInfo);
BEGIN
{$IFC qDebug}
	IF gTopHandler <> @fi THEN
	{$IFC UsingMacApp}
		BEGIN
		Write('gTopHandler = ');
		WritePtr(gTopHandler);
		Write('parameter = ');
		WritePtr(@fi);
		WRITELN;
		ProgramBreak('Problem with Success: too many or too few calls to Success');
		END;
	{$ELSEC UsingMacApp}
		Debugger;
	{$ENDC UsingMacApp}
{$ENDC qDebug}

	gTopHandler := fi.nextInfo;
END {Success};

